﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using SFP;
using SFP.Animation2D;

namespace LevelEditor
{
    public partial class LevelEditorForm : Form
    {
        private const int MAX_LAYERS = 10;
        private const int MAX_OBJECTS = 99;

        private Size resolution = new Size(800, 600);
        private Bitmap currentFrame;

        private List<LayerLE> layersLE;
        private LayerLE activeLayerLE;

        private List<ObjectLE> objectsLE;
        private ObjectLE activeObjectLE;
        private long objectLEIDCounter = 0;

        private GameObject selectedGameObject;
        private bool gameObjectGroupPlacing = false;
        private bool dialogFormOpen = false;

        private Color activeColor = Color.FromArgb(16, 16, 56);
        private Color inactiveColor = Color.FromArgb(32, 32, 32);

        private int mouseMoveScrollValue = 5;

        private int rasterSize = 20;
        private int tempRasterSize;
        private Pen rasterPen = new Pen(Color.FromArgb(64, Color.Red));
        private int maxXScroll = 4200;
        private int maxYScroll = 400;

        private LayerManager layerManager;

        public LevelEditorForm()
        {
            InitializeComponent();

            // Game level frame
            currentFrame = new Bitmap(resolution.Width, resolution.Height, PixelFormat.Format32bppPArgb);
            levelPictureBox.Size = resolution;
            levelPictureBox.Image = currentFrame;
            levelPictureBox.KeyDown += new KeyEventHandler(levelPictureBox_KeyDown);

            // Layer-Manager
            layerManager = new LayerManager();
            
            // Level-Editor layers
            layersLE = new List<LayerLE>();
            layersPanel.Controls.Remove(layerPanelDefault);
            addLayer();

            // Level-Editor objects
            objectsLE = new List<ObjectLE>();
            activeObjectLE = null;
            objectsPanel.Controls.Remove(objectPanelDefault);
            selectedGameObject = null;

            // Level-Editor preferences
            rasterTextBox.Text = "" + rasterSize;
            tempRasterSize = rasterSize;
            layerManager.setMaxXScroll(maxXScroll);
            layerManager.setMaxYScroll(maxYScroll);
            maxXScrollTextBox.Text = "" + maxXScroll;
            maxYScrollTextBox.Text = "" + maxYScroll;
            levelHScrollBar.Maximum = (int)Math.Round(layerManager.getMaxXScroll(), 0, MidpointRounding.AwayFromZero) + 9;
            levelVScrollBar.Maximum = (int)Math.Round(layerManager.getMaxYScroll(), 0, MidpointRounding.AwayFromZero) + 9;
            levelVScrollBar.Value = levelVScrollBar.Maximum - 9;
        }

        public Size GetResolution()
        {
            return new Size(resolution.Width, resolution.Height);
        }

        public int GetRasterSize()
        {
            return rasterSize;
        }

        public Pen GetRasterPen()
        {
            return rasterPen;
        }

        public void SetActiveLayerLE(LayerLE layerLE)
        {
            if (activeLayerLE != null) activeLayerLE.GetLayerPanel().BackColor = inactiveColor;
            activeLayerLE = layerLE;
            activeLayerLE.GetLayerPanel().BackColor = activeColor;
            selectedGameObject = null;
        }

        public LayerLE GetActiveLayerLE()
        {
            return activeLayerLE;
        }

        private void addLayerButton_Click(object sender, EventArgs e)
        {
            addLayer();
        }

        private LayerLE addLayer()
        {
            if (layersLE.Count >= MAX_LAYERS) return null;

            Layer layer = new Layer();
            layerManager.addLayer(layer);
            layer.setName("Layer" + (layersLE.Count + 1));
            
            Panel layerPanel = new Panel();
            Label layerNameLabel = new Label();
            CheckBox visibleCheckBox = new CheckBox();
            LayerLE layerLE = new LayerLE(layer, layerPanel, layerNameLabel, visibleCheckBox, this);

            layersPanel.Controls.Add(layerPanel);

            layerPanel.BackColor = inactiveColor;
            layerPanel.BorderStyle = layerPanelDefault.BorderStyle;
            layerPanel.Controls.Add(layerNameLabel);
            layerPanel.Controls.Add(visibleCheckBox);
            int y = layerPanelDefault.Location.Y;
            if (layersLE.Count > 0)
                y = layersLE[layersLE.Count - 1].GetLayerPanel().Location.Y + layerPanelDefault.Height;
            layerPanel.Location = new Point(layerPanelDefault.Location.X, y);
            layerPanel.Name = "layerPanel" + (layersLE.Count + 1);
            layerPanel.Size = layerPanelDefault.Size;
            layerPanel.MouseClick += new MouseEventHandler(layerLE.layerPanel_MouseClick);
            layerPanel.MouseDoubleClick += new MouseEventHandler(layerLE.layerPanel_MouseDoubleClick);

            layerNameLabel.AutoSize = layerNameLabelDefault.AutoSize;
            layerNameLabel.Location = layerNameLabelDefault.Location;
            layerNameLabel.Name = "layerNameLabel" + (layersLE.Count + 1);
            layerNameLabel.Size = layerNameLabelDefault.Size;
            layerNameLabel.Text = layer.getName();
            layerNameLabel.MouseClick += new MouseEventHandler(layerLE.layerPanel_MouseClick);
            layerNameLabel.MouseDoubleClick += new MouseEventHandler(layerLE.layerPanel_MouseDoubleClick);

            visibleCheckBox.AutoSize = visibleCheckBoxDefault.AutoSize;
            visibleCheckBox.Checked = true;
            visibleCheckBox.CheckState = CheckState.Checked;
            visibleCheckBox.Location = visibleCheckBoxDefault.Location;
            visibleCheckBox.Name = "visibleCheckBox" + (layersLE.Count + 1);
            visibleCheckBox.Size = visibleCheckBoxDefault.Size;
            visibleCheckBox.UseVisualStyleBackColor = visibleCheckBoxDefault.UseVisualStyleBackColor;
            visibleCheckBox.CheckedChanged += new EventHandler(layerLE.visibleCheckBox_CheckedChanged);

            if (layersLE.Count == 0) SetActiveLayerLE(layerLE);

            layersLE.Add(layerLE);

            layersPanel.Refresh();

            return layerLE;
        }

        private void deleteLayerButton_Click(object sender, EventArgs e)
        {
            deleteLayerLE(false);
        }

        private void deleteLayerLE(bool deleteAll)
        {
            if (!deleteAll && layersLE.Count == 1) return;

            int index = layersLE.IndexOf(activeLayerLE);
            
            layerManager.removeLayer(activeLayerLE.GetLayer());
            layersPanel.Controls.Remove(activeLayerLE.GetLayerPanel());
            layersLE.Remove(activeLayerLE);

            foreach (KeyValuePair<GameObject, ObjectLE> gameObjObjectLEPair in activeLayerLE.GetGameObjObjectLEDict())
            {
                gameObjObjectLEPair.Value.RemoveLinkedGameObject(gameObjObjectLEPair.Key);
            }

            int y = activeLayerLE.GetLayerPanel().Location.Y;

            for (int i = index; i < layersLE.Count; i++)
            {
                layersLE[i].GetLayerPanel().Location = new Point(layerPanelDefault.Location.X, y + (layerPanelDefault.Height * (i - index)));
            }

            if (layersLE.Count > 0)
            {
                layerPanelDefault = layersLE[0].GetLayerPanel();
                layerNameLabelDefault = layersLE[0].GetLayerNameLabel();
                visibleCheckBoxDefault = layersLE[0].GetVisibleCheckBox();

                SetActiveLayerLE(layersLE[index >= layersLE.Count ? layersLE.Count - 1 : index]);
            }

            selectedGameObject = null;

            layersPanel.Refresh();
        }

        public void SetActiveObjectLE(ObjectLE objectLE)
        {
            if (activeObjectLE != null) activeObjectLE.GetObjectPanel().BackColor = inactiveColor;
            activeObjectLE = objectLE;
            if (activeObjectLE != null)  activeObjectLE.GetObjectPanel().BackColor = activeColor;
            selectedGameObject = null;
        }

        public ObjectLE GetActiveObjectLE()
        {
            return activeObjectLE;
        }

        private void addObjectButton_Click(object sender, EventArgs e)
        {
            addObjectLE();
        }

        private ObjectLE addObjectLE()
        {
            return addObjectLE(objectLEIDCounter);
        }

        private ObjectLE addObjectLE(long id)
        {
            if (objectsLE.Count >= MAX_OBJECTS) return null;

            Panel objectPanel = new Panel();
            Label objectNameLabel = new Label();
            PictureBox objectPictureBox = new PictureBox();
            ObjectLE objectLE = new ObjectLE(id, objectPanel, objectNameLabel, objectPictureBox, this);
            objectLE.Name = "Object" + (objectsLE.Count + 1);
            objectLE.Type = "DefaultType";

            if (id >= objectLEIDCounter) objectLEIDCounter = id + 1;

            objectsPanel.Controls.Add(objectPanel);

            objectPanel.BackColor = inactiveColor;
            objectPanel.BorderStyle = objectPanelDefault.BorderStyle;
            objectPanel.Controls.Add(objectPictureBox);
            objectPanel.Controls.Add(objectNameLabel);
            int y = objectPanelDefault.Location.Y;
            if (objectsLE.Count > 0)
                y = objectsLE[objectsLE.Count - 1].GetObjectPanel().Location.Y + objectPanelDefault.Height;
            objectPanel.Location = new Point(objectPanelDefault.Location.X, y);
            objectPanel.Name = "objectPanel" + (objectsLE.Count + 1);
            objectPanel.Size = objectPanelDefault.Size;
            objectPanel.MouseClick += new MouseEventHandler(objectLE.objectPanel_MouseClick);
            objectPanel.MouseDoubleClick += new MouseEventHandler(objectLE.objectPanel_MouseDoubleClick);

            objectNameLabel.AutoSize = objectNameLabelDefault.AutoSize;
            objectNameLabel.Location = objectNameLabelDefault.Location;
            objectNameLabel.Name = "objectNameLabel" + (objectsLE.Count + 1);
            objectNameLabel.Size = objectNameLabelDefault.Size;
            objectNameLabel.Text = "Object" + (objectsLE.Count + 1);
            objectNameLabel.TextAlign = objectNameLabelDefault.TextAlign;
            objectNameLabel.MouseClick += new MouseEventHandler(objectLE.objectPanel_MouseClick);
            objectNameLabel.MouseDoubleClick += new MouseEventHandler(objectLE.objectPanel_MouseDoubleClick);

            objectPictureBox.BackColor = objectPictureBoxDefault.BackColor;
            objectPictureBox.BorderStyle = objectPictureBoxDefault.BorderStyle;
            objectPictureBox.Location = objectPictureBoxDefault.Location;
            objectPictureBoxDefault.Name = "objectPictureBox" + (objectsLE.Count + 1);
            objectPictureBox.Size = objectPictureBoxDefault.Size;
            objectPictureBox.SizeMode = objectPictureBoxDefault.SizeMode;
            objectPictureBox.TabStop = false;
            objectPictureBox.MouseClick += new MouseEventHandler(objectLE.objectPanel_MouseClick);
            objectPictureBox.MouseDoubleClick += new MouseEventHandler(objectLE.objectPanel_MouseDoubleClick);
            objectPictureBox.MouseDown +=new MouseEventHandler(objectLE.objectPictureBox_MouseDown);

            objectsLE.Add(objectLE);

            objectsPanel.Refresh();

            return objectLE;
        }

        private void deleteObjectButton_Click(object sender, EventArgs e)
        {
            delteObjectLE();
        }

        private void delteObjectLE()
        {
            if (activeObjectLE == null) return;

            int index = objectsLE.IndexOf(activeObjectLE);

            objectsPanel.Controls.Remove(activeObjectLE.GetObjectPanel());

            foreach (KeyValuePair<GameObject, Layer> gameObjLayerPair in activeObjectLE.GetLinkedGameObjects())
            {
                gameObjLayerPair.Value.removeLayerObject(gameObjLayerPair.Key);
            }

            int y = activeObjectLE.GetObjectPanel().Location.Y;
            objectsLE.Remove(activeObjectLE);

            for (int i = index; i < objectsLE.Count; i++)
            {
                objectsLE[i].GetObjectPanel().Location = new Point(objectPanelDefault.Location.X, y + (objectPanelDefault.Height * (i - index)));
            }

            if (objectsLE.Count > 0)
                SetActiveObjectLE(objectsLE[index >= objectsLE.Count ? objectsLE.Count - 1 : index]);
            else
                SetActiveObjectLE(null);

            objectsPanel.Refresh();
        }

        public void scrollToOrigin()
        {
            levelHScrollBar.Value = 0;
            levelVScrollBar.Value = levelVScrollBar.Maximum;

            layerManager.scrollToOrigin();
        }

        private void LevelHScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            layerManager.setXScroll(e.NewValue);
        }

        private void levelVScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            layerManager.setYScroll(levelVScrollBar.Maximum - 9 - e.NewValue);
        }

        private void rasterTextBox_TextChanged(object sender, EventArgs e)
        {
            processIntegerTextBoxChange(rasterTextBox, ref tempRasterSize);
        }

        private void maxXScrollTextBox_TextChanged(object sender, EventArgs e)
        {
            processIntegerTextBoxChange(maxXScrollTextBox, ref maxXScroll);
        }

        private void maxYScrollTextBox_TextChanged(object sender, EventArgs e)
        {
            processIntegerTextBoxChange(maxYScrollTextBox, ref maxYScroll);
        }

        private void processIntegerTextBoxChange(TextBox textBox, ref int param)
        {
            if ("".Equals(textBox.Text)) return;

            try
            {
                param = int.Parse(textBox.Text);
            }
            catch
            {
                textBox.Text = "" + param;
            }
        }

        private void rasterTextBox_Leave(object sender, EventArgs e)
        {
            processIntegerTextBoxLeave(rasterTextBox, ref tempRasterSize);
        }

        private void maxXScrollTextBox_Leave(object sender, EventArgs e)
        {
            processIntegerTextBoxLeave(maxXScrollTextBox, ref maxXScroll);
        }

        private void maxYScrollTextBox_Leave(object sender, EventArgs e)
        {
            processIntegerTextBoxLeave(maxYScrollTextBox, ref maxYScroll);
        }

        private void processIntegerTextBoxLeave(TextBox textBox, ref int param)
        {
            textBox.Text = "" + param;
        }
        
        private void applyButton_Click(object sender, EventArgs e)
        {
            applyLevelPrefs();
        }

        private void applyLevelPrefs()
        {
            rasterSize = int.Parse(rasterTextBox.Text);

            layerManager.setMaxXScroll(maxXScroll);
            layerManager.setMaxYScroll(maxYScroll);
            levelHScrollBar.Maximum = maxXScroll + 9;
            levelHScrollBar.Value = (int)layerManager.getXScroll();
            levelVScrollBar.Maximum = maxYScroll + 9;
            levelVScrollBar.Value = maxYScroll - (int)layerManager.getYScroll();
        }

        public GameObject GetSelectedGameObject()
        {
            return selectedGameObject;
        }

        public void SetDialogFormOpen(bool dialogFormOpen)
        {
            this.dialogFormOpen = dialogFormOpen;
        }

        public bool IsDialogFormOpen()
        {
            return dialogFormOpen;
        }

        private void levelPictureBox_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                Graphics g = Graphics.FromImage(currentFrame);

                g.Clear(Color.Black);
                layerManager.draw(g);
                drawGameObjectSelection(g);
                if (!gameObjectGroupPlacing && !dialogFormOpen) drawImageToPlace(g);
                if (showRasterCheckBox.Checked) drawRaster(g);
            }
            catch (InvalidOperationException) { }

            levelPictureBox.Invalidate();
        }

        private void drawImageToPlace(Graphics g)
        {
            if (activeObjectLE != null && activeObjectLE.ObjectImg != null)
            {
                Bitmap imageToPlace = activeObjectLE.ObjectImg;
                Point mouseLoc = GetCurrentMousePositionOnLevel();
                int imageLocX = GetSnapForX(mouseLoc.X);
                int imageLocY = GetSnapForY(mouseLoc.Y) + imageToPlace.Height;
                
                if (mouseLoc.X >= 0 && mouseLoc.X < levelPictureBox.Width &&
                    mouseLoc.Y >= 0 && mouseLoc.Y < levelPictureBox.Height)
                    g.DrawImage(imageToPlace, imageLocX, levelPictureBox.Height - imageLocY);
            }
        }

        private void drawGameObjectSelection(Graphics g)
        {
            if (selectedGameObject == null) return;

            g.FillRectangle(rasterPen.Brush,
                            (int)Math.Round(selectedGameObject.X, 0, MidpointRounding.AwayFromZero),
                            (int)Math.Round(selectedGameObject.Y - selectedGameObject.Height, 0, MidpointRounding.AwayFromZero),
                            selectedGameObject.Width, selectedGameObject.Height);
        }

        private void drawRaster(Graphics g)
        {
            for (int i = GetRasterXOffset(); i <= resolution.Width; i += rasterSize)
            {
                g.DrawLine(rasterPen, new Point(i, 0), new Point(i, resolution.Height));
            }

            for (int i = resolution.Height - GetRasterYOffset() - 1; i >= 0; i -= rasterSize)
            {
                g.DrawLine(rasterPen, new Point(0, i), new Point(resolution.Width, i));
            }
        }

        public int GetSnapForX(int x)
        {
            return GetSnap(x, GetRasterXOffset());
        }

        public int GetSnapForY(int y)
        {
            return GetSnap(y, GetRasterYOffset());
        }

        private int GetSnap(int value, int rasterOffset)
        {
            if (!snapToRasterCheckBox.Checked) return value;

            int offset = value - rasterOffset;
            if (offset < 0) offset = rasterSize + (offset - rasterSize * (offset / rasterSize));
            int snap = value - offset % rasterSize;
            return snap;
        }

        private int GetRasterXOffset()
        {
            int rasterXOffset = (int)Math.Round(rasterSize - activeLayerLE.GetLayer().getXScroll() % rasterSize, 0, MidpointRounding.AwayFromZero);
            if (rasterXOffset == rasterSize) rasterXOffset = 0;
            return rasterXOffset;
        }

        private int GetRasterYOffset()
        {
            int rasterYOffset = (int)Math.Round(rasterSize - activeLayerLE.GetLayer().getYScroll() % rasterSize, 0, MidpointRounding.AwayFromZero);
            if (rasterYOffset == rasterSize) rasterYOffset = 0;
            return rasterYOffset;
        }

        public Point GetCurrentMousePositionOnLevel()
        {
            return GetMousePositionOnLevel(Cursor.Position);
        }

        public Point GetMousePositionOnLevel(Point mouseLoc)
        {
            int borderWidth = (Width - ClientSize.Width) / 2;
            int titlebarHeight = Height - ClientSize.Height - 2 * borderWidth;

            return new Point(mouseLoc.X - (Location.X + borderWidth + levelPictureBox.Location.X),
                             levelPictureBox.Height - 1 - (mouseLoc.Y - (Location.Y + titlebarHeight + borderWidth + levelPictureBox.Location.Y)));
        }

        public void SetMousePositionOnLevel(Point mouseLocOnLevel)
        {
            int borderWidth = (Width - ClientSize.Width) / 2;
            int titlebarHeight = Height - ClientSize.Height - 2 * borderWidth;

            Cursor.Position = new Point(mouseLocOnLevel.X + (Location.X + borderWidth + levelPictureBox.Location.X),
                             (levelPictureBox.Height - mouseLocOnLevel.Y - 1) + (Location.Y + titlebarHeight + borderWidth + levelPictureBox.Location.Y));
        }

        private void levelPictureBox_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                SetActiveObjectLE(null);
            }
        }

        private void levelPictureBox_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (selectedGameObject != null && activeObjectLE == null)
            {
                new GameObjectPreferencesForm(this).ShowDialog();
            }
        }

        private void levelPictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (activeObjectLE != null && activeObjectLE.ObjectImg != null)
                {
                    if (!gameObjectGroupPlacing)
                    {
                        placeGameObject();
                        gameObjectGroupPlacing = true;
                    }
                }
                else
                {
                    selectGameObject();
                }
            }
        }

        private void levelPictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (gameObjectGroupPlacing)
                {
                    if (!objectGroupingCheckBox.Checked)
                    {
                        for (int row = 0; row < selectedGameObject.GroupRows; row++)
                        {
                            for (int column = 0; column < selectedGameObject.GroupColumns; column++)
                            {
                                if (row == 0 && column == 0) continue;

                                GameObject gameObject = new GameObject();
                                gameObject.Image = selectedGameObject.Image;
                                gameObject.ImagePath = selectedGameObject.ImagePath;
                                gameObject.Name = selectedGameObject.Name;
                                gameObject.Type = selectedGameObject.Type;
                                gameObject.X = selectedGameObject.X + gameObject.Width * column;
                                gameObject.Y = selectedGameObject.Y - gameObject.Height * row;
                                
                                activeLayerLE.GetLayer().addLayerObject(gameObject);
                                activeLayerLE.AddGameObjObjectLELink(gameObject, activeObjectLE);
                                activeObjectLE.AddLinkedGameObject(gameObject, activeLayerLE.GetLayer());
                            }
                        }
                        selectedGameObject.GroupColumns = 1;
                        selectedGameObject.GroupRows = 1;
                    }
                    
                    selectedGameObject = null;
                }
                gameObjectGroupPlacing = false;
                gameObjMouseLocDists = null;
            }
        }

        private void placeGameObject()
        {
            GameObject gameObject = new GameObject();
            gameObject.Image = activeObjectLE.ObjectImg;
            gameObject.ImagePath = activeObjectLE.ObjectImgPath;
            gameObject.Name = activeObjectLE.Name;
            gameObject.Type = activeObjectLE.Type;

            Point gameObjectLocation = GetCurrentMousePositionOnLevel();
            gameObject.X = GetSnapForX(gameObjectLocation.X);
            gameObject.Y = levelPictureBox.Height - GetSnapForY(gameObjectLocation.Y);

            activeLayerLE.GetLayer().addLayerObject(gameObject);
            activeLayerLE.AddGameObjObjectLELink(gameObject, activeObjectLE);
            activeObjectLE.AddLinkedGameObject(gameObject, activeLayerLE.GetLayer());

            selectedGameObject = gameObject;
        }

        private void selectGameObject()
        {
            bool gameObjectFound = false;

            List<LayerObject> gameObjects = activeLayerLE.GetLayer().getLayerObjects();
            for (int i = gameObjects.Count - 1; i >= 0; i--)
            {
                GameObject gameObject = (GameObject)gameObjects[i];

                if (mouseGameObjectCollision(gameObject))
                {
                    selectedGameObject = gameObject;
                    gameObjectFound = true;
                    break;
                }
            }

            if (!gameObjectFound) selectedGameObject = null;
        }

        private bool mouseGameObjectCollision(GameObject gameObject)
        {
            Point mouseLoc = GetCurrentMousePositionOnLevel();
            mouseLoc.Y = levelPictureBox.Height - mouseLoc.Y;

            return (mouseLoc.X >= gameObject.X && mouseLoc.X < gameObject.X + gameObject.Width &&
                    mouseLoc.Y > gameObject.Y - gameObject.Height && mouseLoc.Y <= gameObject.Y);
        }

        private Nullable<Size> gameObjMouseLocDists = null;

        private void levelPictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            levelPictureBox.Focus();

            if (e.Button == MouseButtons.Left)
            {
                Point mouseLoc = GetCurrentMousePositionOnLevel();

                if (mouseLoc.X < 0) SetMousePositionOnLevel(new Point(0, mouseLoc.Y));
                if (mouseLoc.X >= levelPictureBox.Width) SetMousePositionOnLevel(new Point(levelPictureBox.Width - 1, mouseLoc.Y));
                if (mouseLoc.Y < 0) SetMousePositionOnLevel(new Point(mouseLoc.X, 0));
                if (mouseLoc.Y >= levelPictureBox.Height) SetMousePositionOnLevel(new Point(mouseLoc.X, levelPictureBox.Height - 1));

                scrollFromMouseMove();

                if (selectedGameObject != null)
                {
                    if (gameObjectGroupPlacing)
                    {
                        int xDiff = (int)Math.Round(mouseLoc.X - selectedGameObject.X, 0, MidpointRounding.AwayFromZero);
                        int gameObjectColumns = xDiff / selectedGameObject.Image.Width;
                        if (xDiff % selectedGameObject.Image.Width != 0) gameObjectColumns++;

                        int yDiff = (int)Math.Round(selectedGameObject.Y - (levelPictureBox.Height - mouseLoc.Y), 0, MidpointRounding.AwayFromZero);
                        int gameObjectRows = yDiff / selectedGameObject.Image.Height;
                        if (yDiff % selectedGameObject.Image.Height != 0) gameObjectRows++;

                        if (gameObjectColumns > 0) selectedGameObject.GroupColumns = gameObjectColumns;
                        if (gameObjectRows > 0) selectedGameObject.GroupRows = gameObjectRows;
                    }
                    else
                    {
                        if (gameObjMouseLocDists == null)
                            gameObjMouseLocDists = new Size((int)Math.Round(mouseLoc.X - selectedGameObject.X, 0, MidpointRounding.AwayFromZero),
                                                            (int)Math.Round((resolution.Height - mouseLoc.Y) - selectedGameObject.Y, 0, MidpointRounding.AwayFromZero));

                        selectedGameObject.X = GetSnapForX(mouseLoc.X - gameObjMouseLocDists.Value.Width);
                        selectedGameObject.Y = levelPictureBox.Height - GetSnapForY(mouseLoc.Y + gameObjMouseLocDists.Value.Height);
                    }
                }
            }
        }

        private void scrollFromMouseMove()
        {
            Point mouseLoc = GetCurrentMousePositionOnLevel();

            if (mouseLoc.X < 10)
            {
                int newValue = levelHScrollBar.Value - mouseMoveScrollValue;
                if (newValue < 0) newValue = 0;
                levelHScrollBar.Value = newValue;
                layerManager.setXScroll(levelHScrollBar.Value);
            }
            if (mouseLoc.X >= levelPictureBox.Width - 10)
            {
                int newValue = levelHScrollBar.Value + mouseMoveScrollValue;
                if (newValue > levelHScrollBar.Maximum) newValue = levelHScrollBar.Maximum;
                levelHScrollBar.Value = newValue;
                layerManager.setXScroll(levelHScrollBar.Value);
            }
            if (mouseLoc.Y < 10)
            {
                int newValue = levelVScrollBar.Value + mouseMoveScrollValue;
                if (newValue > levelVScrollBar.Maximum) newValue = levelVScrollBar.Maximum;
                levelVScrollBar.Value = newValue;
                layerManager.setYScroll(levelVScrollBar.Maximum - 9 - levelVScrollBar.Value);
            }
            if (mouseLoc.Y >= levelPictureBox.Height - 10)
            {
                int newValue = levelVScrollBar.Value - mouseMoveScrollValue;
                if (newValue < 0) newValue = 0;
                levelVScrollBar.Value = newValue;
                layerManager.setYScroll(levelVScrollBar.Maximum - 9 - levelVScrollBar.Value);
            }
        }

        private void levelPictureBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (selectedGameObject != null)
                {
                    activeLayerLE.GetLayer().removeLayerObject(selectedGameObject);
                    ObjectLE objectLE;
                    activeLayerLE.GetGameObjObjectLEDict().TryGetValue(selectedGameObject, out objectLE);
                    objectLE.RemoveLinkedGameObject(selectedGameObject);
                    activeLayerLE.RemoveGameObjObjectLELink(selectedGameObject);
                    selectedGameObject = null;
                }
            }
        }

        private void levelOriginButton_Click(object sender, EventArgs e)
        {
            scrollToOrigin();
        }

        private void saveButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "XML|*.xml";
            saveFileDialog.Title = "Save preset";
            saveFileDialog.InitialDirectory = Application.StartupPath;
            saveFileDialog.ShowDialog();

            if (saveFileDialog.FileName != "")
            {
                scrollToOrigin();
                createExportXML().Save(saveFileDialog.FileName);
            }
        }

        private XDocument createExportXML()
        {
            XDocument document = new XDocument();

            XElement rootElem = new XElement("Level");
            document.Add(rootElem);

            addAttribute("name", levelTextBox.Text, rootElem);

            XElement resolutionElem = new XElement("Resolution");
            rootElem.Add(resolutionElem);
            addElementForPref("Width", resolution.Width, resolutionElem);
            addElementForPref("Height", resolution.Height, resolutionElem);
            
            addElementForPref("ShowRaster", showRasterCheckBox.Checked, rootElem);
            addElementForPref("SnapToRaster", snapToRasterCheckBox.Checked, rootElem);
            addElementForPref("ObjectGrouping", objectGroupingCheckBox.Checked, rootElem);
            addElementForPref("MaxXScroll", maxXScroll, rootElem);
            addElementForPref("MaxYScroll", maxYScroll, rootElem);
            addElementForPref("RasterSize", rasterSize, rootElem);

            XElement layersElem = new XElement("Layers");
            rootElem.Add(layersElem);

            Dictionary<Layer, Dictionary<GameObject, long>> layersDictionary = new Dictionary<Layer, Dictionary<GameObject, long>>();

            foreach (Layer layer in layerManager.getLayers())
            {
                Dictionary<GameObject, long> gameObjectDict = new Dictionary<GameObject, long>();
                layersDictionary.Add(layer, gameObjectDict);
            }

            foreach (ObjectLE objectLE in objectsLE)
            {
                foreach (KeyValuePair<GameObject, Layer> gameObjLayerPair in objectLE.GetLinkedGameObjects())
                {
                    if (layersDictionary.ContainsKey(gameObjLayerPair.Value))
                    {
                        Dictionary<GameObject, long> gameObjectDict;
                        layersDictionary.TryGetValue(gameObjLayerPair.Value, out gameObjectDict);
                        gameObjectDict.Add(gameObjLayerPair.Key, objectLE.id);
                    }
                }
            }

            foreach (KeyValuePair<Layer, Dictionary<GameObject, long>> layerGameObjsPair in layersDictionary)
            {
                XElement layerElem = new XElement("Layer");

                addElementForPref("Name", layerGameObjsPair.Key.getName(), layerElem);
                addElementForPref("ZDepth", GetStringFromDouble(layerGameObjsPair.Key.getZDepth()), layerElem);
                addElementForPref("Scrollable", layerGameObjsPair.Key.isScrollable(), layerElem);
                addElementForPref("Visible", layerGameObjsPair.Key.isVisible(), layerElem);

                XElement gameObjectsElem = new XElement("GameObjects");

                foreach (KeyValuePair<GameObject, long> gameObjObjectLEIDPair in layerGameObjsPair.Value)
                {
                    GameObject gameObj = gameObjObjectLEIDPair.Key;
                    XElement gameObjElem = new XElement("GameObject");

                    addAttribute("objectID", gameObjObjectLEIDPair.Value, gameObjElem);
                    addElementForPref("X", GetStringFromDouble(gameObj.X), gameObjElem);
                    addElementForPref("Y", GetStringFromDouble(gameObj.Y), gameObjElem);
                    addElementForPref("GroupColumns", gameObj.GroupColumns, gameObjElem);
                    addElementForPref("GroupRows", gameObj.GroupRows, gameObjElem);

                    gameObjectsElem.Add(gameObjElem);
                }

                layerElem.Add(gameObjectsElem);

                layersElem.Add(layerElem);
            }

            XElement objectsElem = new XElement("Objects");
            rootElem.Add(objectsElem);

            foreach (ObjectLE objectLE in objectsLE)
            {
                XElement objectElem = new XElement("Object");

                addAttribute("objectID", objectLE.id, objectElem);
                addElementForPref("Name", objectLE.Name, objectElem);
                addElementForPref("Type", objectLE.Type, objectElem);
                addElementForPref("ImgPath", objectLE.ObjectImgPath, objectElem);

                objectsElem.Add(objectElem);
            }

            return document;
        }

        private XElement addElementForPref(string name, object value, XElement parentElem)
        {
            XElement paramElem = new XElement(name);
            paramElem.SetValue(value);
            parentElem.Add(paramElem);

            return paramElem;
        }

        private void addAttribute(string name, object value, XElement parentElem)
        {
            XAttribute attribute = new XAttribute(name, value);
            parentElem.Add(attribute);
        }

        private string GetStringFromDouble(double value)
        {
            return ("" + value).Replace('.', ',');
        }

        private void loadButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "Load a preset";
            openFileDialog.InitialDirectory = Application.StartupPath;
            openFileDialog.Multiselect = false;
            openFileDialog.Filter = "XML|*.xml";

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                XDocument document = null;
                try
                {
                    document = XDocument.Load(openFileDialog.FileName);
                }
                catch (XmlException)
                {
                    SendKeys.Send("%");
                    MessageBox.Show("Couldn't open XML file!", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }

                if (document != null)
                {
                    loadXML(document);
                    applyLevelPrefs();
                }
            }
        }

        private void loadXML(XDocument document)
        {
            resetLevel(true);
            XElement rootElem = document.Root;

            if (rootElem.Attribute("name") != null) levelTextBox.Text = rootElem.Attribute("name").Value;
            SetCheckBoxFromXmlElem("ShowRaster", ref showRasterCheckBox, true, rootElem);
            SetCheckBoxFromXmlElem("SnapToRaster", ref snapToRasterCheckBox, true, rootElem);
            SetCheckBoxFromXmlElem("ObjectGrouping", ref objectGroupingCheckBox, true, rootElem);
            SetIntPrefFromXmlElem("MaxXScroll", ref maxXScroll, 0, rootElem);
            SetIntPrefFromXmlElem("MaxYScroll", ref maxYScroll, 0, rootElem);
            SetIntPrefFromXmlElem("RasterSize", ref rasterSize, 20, rootElem);
            maxXScrollTextBox.Text = "" + maxXScroll;
            maxYScrollTextBox.Text = "" + maxYScroll;
            rasterTextBox.Text = "" + rasterSize;

            Dictionary<long, ObjectLE> objectsLEDict = new Dictionary<long, ObjectLE>();

            XElement objectsElem = rootElem.Element("Objects");
            if (objectsElem != null)
            {
                foreach (XElement objectElem in objectsElem.Elements("Object"))
                {
                    XAttribute idAttribute = objectElem.Attribute("objectID");
                    if (idAttribute != null)
                    {
                        long objectID = GetLongFromString(idAttribute.Value, 0);
                        if (objectsLEDict.ContainsKey(objectID)) continue;
                        ObjectLE objectLE = addObjectLE(objectID);

                        foreach (XElement objectPrefElem in objectElem.Elements())
                        {
                            if ("Name".Equals(objectPrefElem.Name.LocalName))
                            {
                                objectLE.Name = objectPrefElem.Value;
                                objectLE.GetObjectNameLabel().Text = objectLE.Name;
                            }
                            else if ("Type".Equals(objectPrefElem.Name.LocalName))
                            {
                                objectLE.Type = objectPrefElem.Value;
                            }
                            else if ("ImgPath".Equals(objectPrefElem.Name.LocalName))
                            {
                                string relativePath = objectPrefElem.Value;
                                string absolutePath = Application.StartupPath + "\\" + relativePath;

                                Bitmap image = null;
                                try
                                {
                                    image = new Bitmap(absolutePath);
                                    image.SetResolution(96.0f, 96.0f);
                                }
                                catch (Exception) { }

                                if (image != null)
                                {
                                    objectLE.ObjectImgPath = relativePath;
                                    objectLE.ObjectImgFileName = Path.GetFileName(absolutePath);
                                    objectLE.ObjectImg = image;
                                    objectLE.GetObjectPictureBox().Image = image;
                                }
                            }
                        }

                        objectsLEDict.Add(objectID, objectLE);
                    }
                }
            }

            XElement layersElem = rootElem.Element("Layers");
            if (layersElem != null)
            {
                foreach (XElement layerElement in layersElem.Elements("Layer"))
                {
                    LayerLE layerLE = addLayer();

                    foreach (XElement layerPrefElement in layerElement.Elements())
                    {
                        if ("Name".Equals(layerPrefElement.Name.LocalName))
                        {
                            layerLE.GetLayer().setName(layerPrefElement.Value);
                            layerLE.GetLayerNameLabel().Text = layerLE.GetLayer().getName();
                        }
                        else if ("ZDepth".Equals(layerPrefElement.Name.LocalName))
                        {
                            layerLE.GetLayer().setZDepth(GetDoubleFromString(layerPrefElement.Value, 0));
                        }
                        else if ("Scrollable".Equals(layerPrefElement.Name.LocalName))
                        {
                            layerLE.GetLayer().setScrollable(GetBoolFromString(layerPrefElement.Value));
                        }
                        else if ("Visible".Equals(layerPrefElement.Name.LocalName))
                        {
                            layerLE.GetLayer().setVisible(GetBoolFromString(layerPrefElement.Value));
                            layerLE.GetVisibleCheckBox().Checked = layerLE.GetLayer().isVisible();
                        }
                        else if ("GameObjects".Equals(layerPrefElement.Name.LocalName))
                        {
                            foreach (XElement gameObjElement in layerPrefElement.Elements("GameObject"))
                            {
                                XAttribute idAttribute = gameObjElement.Attribute("objectID");
                                if (idAttribute != null)
                                {
                                    long objectID = GetLongFromString(idAttribute.Value, 0);
                                    if (!objectsLEDict.ContainsKey(objectID)) continue;

                                    ObjectLE objectLE;
                                    objectsLEDict.TryGetValue(objectID, out objectLE);
                                    
                                    GameObject gameObj = new GameObject();
                                    gameObj.Name = objectLE.Name;
                                    gameObj.Type = objectLE.Type;
                                    gameObj.Image = objectLE.ObjectImg;
                                    gameObj.ImagePath = objectLE.ObjectImgPath;

                                    objectLE.AddLinkedGameObject(gameObj, layerLE.GetLayer());
                                    layerLE.AddGameObjObjectLELink(gameObj, objectLE);
                                    layerLE.GetLayer().addLayerObject(gameObj);

                                    foreach (XElement gameObjPrefElement in gameObjElement.Elements())
                                    {
                                        if ("X".Equals(gameObjPrefElement.Name.LocalName))
                                        {
                                            gameObj.X = GetDoubleFromString(gameObjPrefElement.Value, 0);
                                        }
                                        else if ("Y".Equals(gameObjPrefElement.Name.LocalName))
                                        {
                                            gameObj.Y = GetDoubleFromString(gameObjPrefElement.Value, 0);
                                        }
                                        else if ("GroupColumns".Equals(gameObjPrefElement.Name.LocalName))
                                        {
                                            gameObj.GroupColumns = GetIntFromString(gameObjPrefElement.Value, 1);
                                        }
                                        else if ("GroupRows".Equals(gameObjPrefElement.Name.LocalName))
                                        {
                                            gameObj.GroupRows = GetIntFromString(gameObjPrefElement.Value, 1);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (layersLE.Count == 0) addLayer();
        }

        private void SetIntPrefFromXmlElem(string name, ref int pref, int defaultValue, XElement parentElem)
        {
            XElement prefElem = parentElem.Element(name);
            if (prefElem != null) pref = GetIntFromString(prefElem.Value, defaultValue);
            else pref = defaultValue;
        }

        private void SetCheckBoxFromXmlElem(string name, ref CheckBox checkBox, bool defaultValue, XElement parentElem)
        {
            XElement prefElem = parentElem.Element(name);
            if (prefElem != null) checkBox.Checked = GetBoolFromString(prefElem.Value);
            else checkBox.Checked = defaultValue;
        }

        private int GetIntFromString(string value, int defaultValue)
        {
            try { return int.Parse(value); }
            catch (FormatException) { return defaultValue; }
        }

        private long GetLongFromString(string value, long defaultValue)
        {
            try { return long.Parse(value); }
            catch (FormatException) { return defaultValue; }
        }

        private double GetDoubleFromString(string value, double defaultValue)
        {
            try { return double.Parse(value); }
            catch (FormatException) { return defaultValue; }
        }

        private bool GetBoolFromString(string value)
        {
            try { return bool.Parse(value); }
            catch (FormatException) { return false; }
        }

        public void resetLevel(bool noDefaultLayer)
        {
            scrollToOrigin();

            layerManager.clearManager();

            List<LayerLE> layersLE = new List<LayerLE>(this.layersLE);
            foreach (LayerLE layerLE in layersLE)
            {
                activeLayerLE = layerLE;
                deleteLayerLE(true);
            }
            if (!noDefaultLayer) addLayer();

            List<ObjectLE> objectsLE = new List<ObjectLE>(this.objectsLE);
            foreach (ObjectLE objectLE in objectsLE)
            {
                activeObjectLE = objectLE;
                delteObjectLE();
            }
            activeObjectLE = null;

            selectedGameObject = null;
            objectLEIDCounter = 0;

            levelTextBox.Text = "NewLevel";
            showRasterCheckBox.Checked = true;
            snapToRasterCheckBox.Checked = true;
            objectGroupingCheckBox.Checked = true;
            tempRasterSize = 20;
            rasterTextBox.Text = "" + tempRasterSize;
            maxXScroll = 4200;
            maxXScrollTextBox.Text = "" + maxXScroll;
            maxYScroll = 400;
            maxYScrollTextBox.Text = "" + maxYScroll;
            applyLevelPrefs();
        }

        private void newButton_Click(object sender, EventArgs e)
        {
            resetLevel(false);
        }

        private void layerUpButton_Click(object sender, EventArgs e)
        {
            int index = layersLE.IndexOf(activeLayerLE);
            if (index == 0) return;
            LayerLE swapLayerLE = layersLE[index - 1];
            layersLE[index - 1] = activeLayerLE;
            layersLE[index] = swapLayerLE;
            Point layerLELocaction = activeLayerLE.GetLayerPanel().Location;
            Point swapLayerLELocaction = swapLayerLE.GetLayerPanel().Location;
            activeLayerLE.GetLayerPanel().Location = swapLayerLELocaction;
            swapLayerLE.GetLayerPanel().Location = layerLELocaction;
            layerManager.moveLayerBack(activeLayerLE.GetLayer());
        }

        private void layerDownButton_Click(object sender, EventArgs e)
        {
            int index = layersLE.IndexOf(activeLayerLE);
            if (index == layersLE.Count - 1) return;
            LayerLE swapLayerLE = layersLE[index + 1];
            layersLE[index + 1] = activeLayerLE;
            layersLE[index] = swapLayerLE;
            Point layerLELocaction = activeLayerLE.GetLayerPanel().Location;
            Point swapLayerLELocaction = swapLayerLE.GetLayerPanel().Location;
            activeLayerLE.GetLayerPanel().Location = swapLayerLELocaction;
            swapLayerLE.GetLayerPanel().Location = layerLELocaction;
            layerManager.moveLayerForward(activeLayerLE.GetLayer());
        }
    }
}
